package com.starsky.common.framework.expect;

import com.alibaba.fastjson.JSONObject;
import com.starsky.common.data.RstData;
import com.starsky.common.enums.impl.EnumResult;
import com.starsky.common.exception.ExceptionInfoType;
import com.starsky.common.framework.expect.config.LogConfig;
import com.starsky.common.framework.expect.model.AspectLoggerDto;
import com.starsky.common.framework.expect.service.LogService;
import com.starsky.common.utils.DateUtils;
import com.starsky.common.utils.UUIDUtils;
import com.starsky.common.utils.os.SystemUtils;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.List;
import java.util.Locale;

@Slf4j
@Aspect
@Component
public class AspectLogger {

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Autowired
    private LogService logService;
    //配置信息
    @Autowired
    private LogConfig logConfig;
    @Autowired
    private HttpServletRequest request;
    //服务名称
    @Value("${spring.application.name}")
    private String serverName;


    //申明一个切点  里面是execution  表达式,注意这里不能把切点自己给包含了，否则造成死循环
    @Pointcut(value = "(execution(* com.starsky..*.controller.*.*(..))))")
    private void pointcut() {
    }

    //环绕通知
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {

        Long startTime = System.currentTimeMillis();
        // 定义日志对象Dto
        AspectLoggerDto ald = new AspectLoggerDto();
        // 默认操作日志
//        LogServiceUrlEnum urlEnum = LogServiceUrlEnum.REQ_SERVICE_OPER_URL;
        int logType = 1;  //1-操作日志保存；2-系统异常日志；3-业务异常日志；4-登陆日志
        try {

            ald.setAppNo(logConfig.getAppNo());
            ald.setAppName(logConfig.getAppName());
            ald.setExecLogId(UUIDUtils.getSerial("LOG", 7));
            ald.setStartTime(DateUtils.getDateTime(startTime, "yyyy-MM-dd HH:mm:ss:SSS"));
            ald.setUserId(request.getHeader("userId"));
            ald.setUserName(request.getHeader("userName"));
            ald.setServerName(serverName);
            ald.setOpertionTime(new Date());
            ald.setSessionId(request.getSession().getId());

            MDC.put("logId", ald.getExecLogId());
            // 获取注解
            ApiOperation apiOperation = ((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(ApiOperation.class);
            if (apiOperation != null) {
                ald.setOpertionName(apiOperation.value());
                String notes = apiOperation.notes();
                ald.setOpertionDesc(StringUtils.isBlank(notes) ? apiOperation.value() : notes);
                ald.setMethodDesc(notes);
            }
            //1-操作日志保存；2-系统异常日志；3-业务异常日志；4-登陆日志
            ald.setLogType(1);
            ald.setReqIp(SystemUtils.getRemoteAddr(request));
            String requestURI = request.getRequestURI();
            String localAddr = request.getLocalAddr();
            int localPort = request.getLocalPort();
            System.out.println("requestURI: " + requestURI);
            System.out.println("localAddr: " + localAddr);
            System.out.println("localPort: " + localPort);

            String remoteAddr = request.getRemoteAddr();
            String remoteHost = request.getRemoteHost();
            int remotePort = request.getRemotePort();
            System.out.println("remoteAddr: " + remoteAddr);
            System.out.println("remoteHost: " + remoteHost);
            System.out.println("remotePort: " + remotePort);

            String queryString = request.getQueryString();
            String remoteUser = request.getRemoteUser();
            int serverPort = request.getServerPort();
            System.out.println("queryString: " + queryString);
            System.out.println("remoteUser: " + remoteUser);
            System.out.println("serverPort: " + serverPort);

            String requestURL = request.getRequestURL().toString();
            ald.setReqUrl(requestURL);
            ald.setMethodType(request.getMethod());
            String contentType = request.getContentType();
            contentType = (StringUtils.isEmpty(contentType)) ? "application/x-www-form-urlencoded" : contentType;
            ald.setContentType(contentType);
            String characterEncoding = request.getCharacterEncoding();
            characterEncoding = (StringUtils.isEmpty(characterEncoding)) ? "ISO-8859-1" : characterEncoding;
            ald.setCharacterSet(characterEncoding);
            Locale locale = request.getLocale();
            ald.setLocale(locale.toLanguageTag());
            String userAgent = request.getHeader("User-Agent");
            ald.setUserAgent(userAgent);
            ald.setClassName(pjp.getSignature().getDeclaringTypeName());
            ald.setMethodName(pjp.getSignature().getName());
            ald.setExecServerIp(SystemUtils.getLocalIp());
            ald.setExecServerTime(new Date());

            try {
                Object[] args = pjp.getArgs();
                if (args != null && args.length > 0) {
                    ald.setReqParam(JSONObject.toJSONString(args));
                }
            } catch (Exception e) {
                log.error("参数转换异常: ", e.getMessage());
                ald.setRespCode(EnumResult.FAILED.getCode());
                ald.setRespMsg("参数转换异常: " + e.getMessage());
                ald.setExcepRst("参数转换异常: " + e.getMessage());
            }
            //打印内容
            log.debug("===============================[Method-Api Start]===============================");
            log.debug("[请求IP:{}]", ald.getReqIp());
            log.debug("[请求地址:{}]", ald.getReqUrl());
            log.debug("[请求方式:{}]", ald.getMethodType());
            log.debug("[请求方法: {}]", ald.getClassName() + "." + ald.getMethodName());
            log.debug("[请求参数:{}]", ald.getReqParam());
            log.debug("======方法执行 Start======");

            Object object = null;

            //是否开启权限认证
            Boolean isAuth = logConfig.getIsAuth();
//            if (isAuth != null && isAuth == true) {
//                //请求放行，无需验证权限
//                if (pathMatcher(requestURI)) {
//                    object = pjp.proceed();
//                } else {
//                    String token = request.getHeader("token");
//                    if (StringUtils.isEmpty(token)) {
//                        return RstData.faild(EnumResult.AUTH.getCode(), "用户未登录或者登录已失效");
//                    }
//                    Long userId = SecurityUser.getUserId();
//                    if (userId == null || userId <= 0) {
//                        return RstData.faild(EnumResult.AUTH.getCode(), "用户未登录或者登录已失效");
//                    }
//                    //根据token获取redis用户信息 todo
//                    object = pjp.proceed();
//                }
//            } else {
            //不验证权限，执行所有方法
            object = pjp.proceed();
//            }

            log.debug("======方法执行 End======");
            RstData rstData = RstData.success();
            if (object instanceof RstData) {
                //如果是自定义返回结果集合
                rstData = (RstData) object;
            }
//            ald.setResParam(JSONObject.toJSONString(rstData));
            ald.setRespCode(rstData.getCode());
            ald.setRespMsg(rstData.getMsg());
            Long endTime = System.currentTimeMillis();
            ald.setEndTime(DateUtils.getDateTime(endTime, "yyyy-MM-dd HH:mm:ss:SSS"));
            ald.setUseTime(String.valueOf(endTime - startTime));

            log.debug("[返回参数:{}]", rstData);
            log.debug("[执行时间:{}ms]", ald.getUseTime());

            return object;

        } catch (Throwable e) {
            log.debug("======方法执行 End======");
            if (e instanceof ExceptionInfoType) {
                //1-操作日志保存；2-系统异常日志；3-业务异常日志；4-登陆日志
                logType = 3;
//                urlEnum = LogServiceUrlEnum.REQ_SERVICE_BUSSEXCEP_URL;
                log.error("业务异常:", e);
                RstData rstData = RstData.faild(((ExceptionInfoType) e));
                ald.setLogType(3);
                ald.setRespCode(rstData.getCode());
                ald.setRespMsg(rstData.toString());
                ald.setExcepRst(e.getMessage());
                Long endTime = System.currentTimeMillis();
                ald.setEndTime(DateUtils.getDateTime(endTime, "yyyy-MM-dd HH:mm:ss:SSS"));
                ald.setUseTime(String.valueOf(endTime - startTime));

                log.debug("[返回参数:{}]", rstData);
                log.debug("[执行时间:{}ms]", ald.getUseTime());

                return rstData;
            }
            //1-操作日志保存；2-系统异常日志；3-业务异常日志；4-登陆日志
            logType = 2;
//            urlEnum = LogServiceUrlEnum.REQ_SERVICE_EXCEP_URL;
            log.error("系统异常:", e);
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw, true));
            RstData rstData = RstData.faild(EnumResult.FAILED, "方法执行异常");

            ald.setLogType(2);
            ald.setRespCode(rstData.getCode());
            ald.setRespMsg(rstData.toString());
            ald.setExcepRst(e.getMessage());
            Long endTime = System.currentTimeMillis();
            ald.setEndTime(DateUtils.getDateTime(endTime, "yyyy-MM-dd HH:mm:ss:SSS"));
            ald.setUseTime(String.valueOf(endTime - startTime));

            log.debug("[返回参数:{}]", rstData);
            log.debug("[执行时间:{}ms]", ald.getUseTime());

            return rstData;

        } finally {
            log.debug("===============================[Method-Api End]===============================");
            try {
                MDC.clear();
                logService.sendLog(request.getRequestURI(), ald, logType);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 不拦截url路径
     *
     * @param requestUri
     * @return
     */
    private boolean pathMatcher(String requestUri) {
        List<String> urls = logConfig.getUrls();
        for (String url : urls) {
            if (antPathMatcher.match(url, requestUri)) {
                return true;
            }
        }
        return false;
    }
}

